#include "simulator/report/report.h"
#include "simulator/bitTools/bitTools.h"
#include "dspSimulator/dspSimulator/dspInstFetcher.h"

std::vector<u8> DspInstFetcher::readBytesFromFetchPacket(u32 offset, u32 size) {
    if (offset >= 0x20 || offset + size - 1 >= 0x20)
        fatalMacro("试图取出超过范围的指令数据！");
    std::vector<u8> res;
    for (u32 i = offset; i < offset + size; i++)
        res.push_back(m_fetchPacket[i]);
    return std::move(res);
}

void DspInstFetcher::updateFetchPacket(u32 beginAddr) {
    m_beginAddr = beginAddr;
    m_fetchPacket.clear();
    m_headWord.reset();
    auto memory = m_targetMachine->getMMU()->getMemory();
    // 一次读八个字,32个字节
    m_fetchPacket = memory->readNBytes(m_beginAddr, 32);
    // 读取当前指令包的最后4个字节
    auto lastByte = readBytesFromFetchPacket(0x20 - 4, 4);
    // 如果最后一个字节的高4bit为1110，则当前指令包为compact指令包
    if ((lastByte[3] & 0b11110000) == 0b11100000) {
        m_isStandard = false;
        m_headWord = vectorToBitSet<32>(lastByte);
    } else {
        m_isStandard = true;
    }
}

std::shared_ptr<InstInfo> DspInstFetcher::fetchOneInstruction() {
    // 如果锁存，无法从内存中取出指令数据，返回空
    if (!m_enableMemory) {
        return nullptr;
    }
    // 获取pc寄存器的当前值
    auto pcReg = m_targetMachine->getRegisters()->getReg32ByName("pc");
    auto pc = *pcReg->getDataPtr();
    // 获取当前取指包的首地址值
    u32 beginAddr = pc - pc % 0x20;
    // 如果当前需要的指令在新的取指包中，更新当前的取指包
    if (beginAddr != m_beginAddr)
        updateFetchPacket(beginAddr);
    // 取出当前pc寄存器指向的指令
    auto instruction = fetchOneInstruction(pc);
    return instruction;
}


std::shared_ptr<InstInfo> DspInstFetcher::fetchOneInstruction(u32 address) {
    if (address < m_beginAddr || address >= m_beginAddr + 0x20)
        fatalMacro("取指失败，试图取出不在当前指令包的指令!");
    // 计算当前取指位置在取指包中的偏移量
    u32 offset = address % 0x20;
    auto pcReg = m_targetMachine->getRegisters()->getReg32ByName("pc");
    std::vector<u8> bits;
    // 取指包为Standard C6000 Fetch Packet
    if (m_isStandard) {
        // 取出一个四字节指令
        bits = readBytesFromFetchPacket(offset, 4);
        std::shared_ptr<InstInfo> oneInst = std::make_shared<DspInstInfo>(bits, *pcReg->getDataPtr(), true);
        pcReg->updateData(*pcReg->getDataPtr() + 4);
        return oneInst;
    } else {
        // Header-Based Fetch Packet
        // 如果当前pc指向headWord，更新pc的值为headWord的下一条指令，并取出下一条指令的指令bits
        if (offset >> 2 == 7) {
            pcReg->updateData(*pcReg->getDataPtr() + 4);
            return fetchOneInstruction();
        }
        // 获取headword的指令布局字段
        auto layout = getLayoutWord();
        if (layout[offset >> 2] == 0) {
            // a single 32-bit opcode.
            bits = readBytesFromFetchPacket(offset, 4);
            std::shared_ptr<InstInfo> oneInst = std::make_shared<DspInstInfo>(bits, *pcReg->getDataPtr(), false);
            pcReg->updateData(*pcReg->getDataPtr() + 4);
            return oneInst;
        } else {
            // compact instruction
            bits = readBytesFromFetchPacket(offset, 2);
            std::shared_ptr<InstInfo> oneInst = std::make_shared<DspInstInfo>(bits, *pcReg->getDataPtr(), false,
                                                                              getPROT(), getRS(), getDSZ(), getBR(),
                                                                              getSAT(), isParallelCompactInst(
                            *pcReg->getDataPtr()));
            pcReg->updateData(*pcReg->getDataPtr() + 2);
            return oneInst;
        }
    }
}

std::bitset<7> DspInstFetcher::getLayoutWord() const {
    std::bitset<7> res;
    for (u32 i = 0; i < 7; i++)
        res[i] = m_headWord[i + 21];
    return res;
}

bool DspInstFetcher::getPROT() const {
    return m_headWord[20];
}

bool DspInstFetcher::getSAT() const {
    return m_headWord[14];
}

bool DspInstFetcher::getRS() const {
    return m_headWord[19];
}

bool DspInstFetcher::getBR() const {
    return m_headWord[15];
}

bool DspInstFetcher::isStandard() const {
    return m_isStandard;
}

std::bitset<3> DspInstFetcher::getDSZ() const {
    std::bitset<3> res;
    for (u32 i = 0; i < 3; i++)
        res[i] = m_headWord[16 + i];
    return res;
}

bool DspInstFetcher::isParallelCompactInst(u32 address) const {
    auto offset = address % 0x20;
    return m_headWord[offset >> 1];
}

void DspInstFetcher::setFetchState(bool enableMemory) {
    m_enableMemory = enableMemory;
}

